PM2 部署 Django 项目
一、核心要点速览
💡 核心考点
- 部署方案: Gunicorn/uWSGI + PM2 进程管理
- 配置文件: ecosystem.config.js 指定 Python 解释器
- 虚拟环境: 正确配置 PYTHONPATH 和 PATH
- Nginx 反向代理: 处理静态文件和 HTTPS
- 自动化部署: deploy.sh 脚本一键部署
二、Gunicorn + PM2 方案
1. 完整部署流程
bash
# ========== 步骤 1: 安装依赖 ==========
cd /var/www/my-django-app
# 创建虚拟环境
python3 -m venv venv
source venv/bin/activate
# 安装 Gunicorn 和依赖
pip install gunicorn django
# ========== 步骤 2: 创建配置文件 ==========
cat > ecosystem.config.js << 'EOF'
module.exports = {
apps: [{
name: 'my-django-app',
script: './venv/bin/gunicorn',
args: 'myproject.wsgi:application --bind 127.0.0.1:8000 --workers 4',
interpreter: 'python3',
env: {
DJANGO_SETTINGS_MODULE: 'myproject.settings.production',
PYTHONPATH: '/var/www/my-django-app',
PATH: '/var/www/my-django-app/venv/bin:/usr/local/bin:/usr/bin:/bin'
},
log_date_format: 'YYYY-MM-DD HH:mm:ss',
error_file: './logs/gunicorn-error.log',
out_file: './logs/gunicorn-out.log',
autorestart: true,
max_restarts: 10,
min_uptime: '10s'
}]
}
EOF
# ========== 步骤 3: 启动应用 ==========
pm2 start ecosystem.config.js
# 验证
pm2 status
pm2 logs my-django-app
# ========== 步骤 4: 配置 Nginx ==========
cat > /etc/nginx/sites-available/django-app << 'EOF'
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/ {
alias /var/www/my-django-app/static/;
}
location /media/ {
alias /var/www/my-django-app/media/;
}
}
EOF
ln -s /etc/nginx/sites-available/django-app /etc/nginx/sites-enabled/
nginx -t
systemctl restart nginx
# ========== 步骤 5: 收集静态文件 ==========
source venv/bin/activate
python manage.py collectstatic --noinput2. 配置详解
┌──────────────────────────────────────────────────────────┐
│ Gunicorn + PM2 配置要点 │
└──────────────────────────────────────────────────────────┘
关键配置项:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
script: './venv/bin/gunicorn'
✓ 指向虚拟环境中的 gunicorn 可执行文件
✓ 确保使用正确的 Python 版本
args: 'myproject.wsgi:application --bind 127.0.0.1:8000 --workers 4'
✓ WSGI 应用入口
✓ 绑定地址和端口
✓ worker 数量(通常是 CPU 核心数 × 2 + 1)
interpreter: 'python3'
✓ 指定 Python 解释器
✓ 必须是 python3 或完整路径
env.PYTHONPATH: '/var/www/my-django-app'
✓ 添加项目根目录到 Python 路径
✓ 确保 Django 能找到项目模块
env.PATH: '/var/www/my-django-app/venv/bin:...'
✓ 优先使用虚拟环境的包
✓ 包含系统路径避免冲突
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━三、uWSGI + PM2 方案
1. 配置步骤
bash
# ========== 安装 uWSGI ==========
pip install uwsgi
# ========== 创建 uWSGI 配置 ==========
cat > uwsgi.ini << 'EOF'
[uwsgi]
chdir = /var/www/my-django-app
module = myproject.wsgi:application
home = /var/www/my-django-app/venv
master = true
processes = 4
threads = 2
socket = 127.0.0.1:8000
chmod-socket = 664
vacuum = true
die-on-term = true
logto = /var/www/my-django-app/logs/uwsgi.log
EOF
# ========== PM2 配置 ==========
cat > ecosystem.config.js << 'EOF'
module.exports = {
apps: [{
name: 'django-uwsgi',
script: '/var/www/my-django-app/venv/bin/uwsgi',
args: './uwsgi.ini',
interpreter: 'python3',
env: {
DJANGO_SETTINGS_MODULE: 'myproject.settings.production',
PYTHONPATH: '/var/www/my-django-app'
},
autorestart: true,
max_restarts: 10
}]
}
EOF
# 启动
pm2 start ecosystem.config.js2. Gunicorn vs uWSGI 对比
| 对比维度 | Gunicorn | uWSGI | 说明 |
|---|---|---|---|
| 配置复杂度 | ⭐⭐⭐⭐⭐ (简单) | ⭐⭐⭐ (复杂) | Gunicorn 开箱即用,uWSGI 配置繁琐 |
| 实现语言 | Python | C | uWSGI 性能更优,但调试困难 |
| 功能丰富度 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | uWSGI 支持更多协议和功能 |
| 资源占用 | 低 | 中 | Gunicorn 更轻量 |
| 社区支持 | 活跃 | 成熟 | 两者都有良好生态 |
| 学习曲线 | 平缓 | 陡峭 | Gunicorn 上手更快 |
| 适用规模 | 中小型项目 | 大型项目 | 按项目规模选择 |
Gunicorn 特点:
| 优势 ✓ | 劣势 ✗ |
|---|---|
| 配置简单,开箱即用 | 功能相对单一 |
| 纯 Python 实现,易维护 | 不支持 WebSocket 等高级协议 |
| 性能优秀,资源占用少 | 监控和管理功能较弱 |
| 社区活跃,文档完善 | - |
uWSGI 特点:
| 优势 ✓ | 劣势 ✗ |
|---|---|
| 功能丰富,配置灵活 | 配置复杂,学习曲线陡 |
| 支持多种协议(HTTP、WebSocket) | 调试和排查问题困难 |
| 强大的监控和管理功能 | 文档繁多,难以全面掌握 |
| C 语言实现,性能极致 | 资源占用相对较高 |
推荐选择:
| 项目类型 | 推荐方案 | 理由 |
|---|---|---|
| 中小型项目 | Gunicorn | 简单够用,快速部署 |
| 大型项目 | uWSGI | 功能强大,可扩展性强 |
| 团队熟悉 Python | Gunicorn | 易于维护和调试 |
| 有专业运维团队 | uWSGI | 发挥全部功能潜力 |
四、自动化部署脚本
1. deploy.sh
bash
#!/bin/bash
# deploy.sh - Django 项目自动化部署脚本
set -e
PROJECT_NAME="my-django-app"
PROJECT_DIR="/var/www/$PROJECT_NAME"
PYTHON_VERSION="3.10"
echo "🚀 开始部署 Django 项目..."
# 进入项目目录
cd $PROJECT_DIR
# 拉取最新代码
git pull origin main
# 更新虚拟环境
python$PYTHON_VERSION -m venv venv
source venv/bin/activate
# 安装依赖
pip install --upgrade pip
pip install -r requirements.txt
# 数据库迁移
python manage.py migrate
# 收集静态文件
python manage.py collectstatic --noinput
# 重启应用
pm2 restart $PROJECT_NAME
# 清理旧版本
pip cache purge
echo "✅ 部署完成!"
pm2 status $PROJECT_NAME2. 使用方法
bash
# 赋予执行权限
chmod +x deploy.sh
# 执行部署
./deploy.sh
# 输出示例:
# 🚀 开始部署 Django 项目...
# Already up to date.
# Requirement already satisfied: django in ./venv
# Applying blog.0001_initial... OK
# Collecting static files... OK
# ✅ 部署完成!
# ┌────┬─────────────┬──────────┬──────┬───────────┬──────────┐
# │ id │ name │ mode │ ↺ │ status │ cpu │
# ├────┼─────────────┼──────────┼──────┼───────────┼──────────┤
# │ 0 │ my-django-… │ fork │ 10 │ online │ 0% │
# └────┴─────────────┴──────────┴──────┴───────────┴──────────┘五、Django 项目结构
/var/www/my-django-app/
├── manage.py
├── requirements.txt
├── ecosystem.config.js # PM2 配置
├── uwsgi.ini # uWSGI 配置(可选)
├── .env # 环境变量
│
├── myproject/ # Django 项目
│ ├── __init__.py
│ ├── settings/
│ │ ├── base.py
│ │ ├── development.py
│ │ └── production.py
│ ├── urls.py
│ └── wsgi.py
│
├── apps/ # 应用目录
│ ├── blog/
│ └── api/
│
├── static/ # 静态文件源
│ ├── css/
│ ├── js/
│ └── images/
│
├── staticfiles/ # collectstatic 目标
│ └── ...
│
├── media/ # 用户上传文件
│ └── uploads/
│
├── logs/ # 日志目录
│ ├── gunicorn-error.log
│ ├── gunicorn-out.log
│ └── django.log
│
└── venv/ # Python 虚拟环境
├── bin/
│ ├── python
│ ├── pip
│ └── gunicorn
└── lib/六、Nginx 配置详解
1. 基础配置
nginx
server {
listen 80;
server_name example.com;
# 最大上传文件大小
client_max_body_size 10M;
# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
location / {
proxy_pass http://127.0.0.1:8000;
# 必需的头部
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持(可选)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# 静态文件
location /static/ {
alias /var/www/my-django-app/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# 媒体文件
location /media/ {
alias /var/www/my-django-app/media/;
expires 7d;
}
# 安全头部
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
}2. HTTPS 配置
nginx
server {
listen 443 ssl http2;
server_name example.com;
# SSL 证书
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL 优化
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
location /static/ {
alias /var/www/my-django-app/static/;
expires 30d;
}
}
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}七、性能优化
1. Gunicorn 优化
bash
# 计算最佳 worker 数量
# 公式:worker 数 = (CPU 核心数 × 2) + 1
# 4 核 CPU 示例
workers = (4 × 2) + 1 = 9
# ecosystem.config.js
args: 'myproject.wsgi:application \
--bind 127.0.0.1:8000 \
--workers 9 \
--worker-class sync \
--timeout 120 \
--keep-alive 5'2. 数据库连接池
python
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'user',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '5432',
'CONN_MAX_AGE': 600, # 连接持久化
'OPTIONS': {
'MAX_CONNS': 20, # 最大连接数
'MIN_CONNS': 5, # 最小连接数
}
}
}3. 缓存配置
python
# settings.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'CONNECTION_POOL_KWARGS': {
'max_connections': 50,
}
}
}
}
# 每页缓存
@cache_page(60 * 15)
def my_view(request):
...八、常见问题解决
1. 启动失败
bash
# 问题 1: 找不到模块
# 检查 PYTHONPATH
export PYTHONPATH=/var/www/my-django-app:$PYTHONPATH
# 问题 2: 权限错误
sudo chown -R $USER:$USER /var/www/my-django-app
chmod -R 755 /var/www/my-django-app
# 问题 3: 端口被占用
lsof -i :8000
kill -9 <PID>
# 修改绑定的端口
args: '--bind 127.0.0.1:8001 ...'2. 静态文件 404
bash
# 确保已收集静态文件
python manage.py collectstatic --noinput
# 检查 Nginx 配置
ls -la /var/www/my-django-app/static/
# 重新加载 Nginx
nginx -t && systemctl reload nginx3. 数据库迁移失败
bash
# 查看迁移状态
python manage.py showmigrations
# 回滚迁移
python manage.py migrate app_name 0001
# 伪造迁移(表已存在)
python manage.py migrate --fake-initial
# 清除迁移缓存
find . -path "*/migrations/*.pyc" -delete九、面试标准回答
使用 PM2 部署 Django 项目通常结合 Gunicorn 或 uWSGI。
基本步骤是:
- 创建 Python 虚拟环境并安装 Gunicorn/uWSGI
- 在
ecosystem.config.js中配置 script 指向 gunicorn,args 指定 WSGI 应用- 正确设置 PYTHONPATH 和 PATH 环境变量
- 配置 Nginx 反向代理和静态文件服务
- 执行
pm2 start启动应用Gunicorn 和 uWSGI 的选择:
- Gunicorn 配置简单,适合中小型项目
- uWSGI 功能强大,适合大型复杂项目
- 两者都能很好地与 PM2 集成
性能优化方面,我会:
- 根据 CPU 核心数设置 worker 数量(核心数×2+1)
- 配置数据库连接池减少开销
- 使用 Redis 缓存热点数据
- 配置 Nginx 缓存静态文件
实际项目中,我编写了自动化部署脚本,实现了 git pull、依赖安装、数据库迁移、静态文件收集和 PM2 重启的一键部署流程,大大提高了部署效率。
十、记忆口诀
部署 Django 歌诀:
Django 部署有方案,
Gunicorn 配 PM2。
虚拟环境要激活,
PATH 变量不能忘!
Nginx 反代配好,
静态文件单独搞。
HTTPS 证书装上,
生产稳定没烦恼!
自动化脚本写,
一键部署效率高。
数据库先迁移,
collectstatic 不能少!
Worker 数量算好,
CPU 核心乘以 2 加 1。
缓存连接池配,
性能优化要做到!十一、推荐资源
十二、总结一句话
- 部署方案: Gunicorn/uWSGI + PM2 = Python 应用标配 🐍
- 配置文件: ecosystem.config.js = 环境变量是关键 ⚙️
- Nginx 代理: 反向代理 + 静态文件 = 性能提升保障 ⚡
- 自动化: deploy.sh 脚本 = 高效部署利器 🚀